package com.hongyun.tms.web.exception;

import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.hongyun.tms.common.enums.BizCode;
import com.hongyun.tms.domain.AjaxResult;
import com.hongyun.tms.exception.BusinessRuntimeException;
import com.hongyun.tms.infra.CollectionValidator;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MaxUploadSizeExceededException;

import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 全局异常处理器
 * <p>
 * 该类用于捕获和处理应用程序中出现的各种异常，并返回适当的错误响应。
 * 它覆盖了多个控制器包，以确保所有相关异常都能被处理。
 *
 * @author lipeng
 * @date 2024-05-31
 */
@ControllerAdvice({
        "com.hongyun.tms.web.controller"
})
@Slf4j
public class ControllerExceptionHandler {

    @Resource
    protected LocalValidatorFactoryBean validator;

    /**
     * 初始化绑定器，添加对POST请求中List参数的校验支持。
     * 例如，对于List类型的参数，默认的javax.validation注解不会生效，通过这个方法可以添加相应的校验。
     *
     * @param binder 用于绑定请求参数的WebDataBinder
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        if (binder.getTarget() instanceof Collection) {
            binder.addValidators(new CollectionValidator(validator));
        }
        // 可以在这里补充其他比如Map等的校验逻辑
    }

    /**
     * 处理所有未捕获的异常。
     * 捕获所有未被其他@ExceptionHandler方法处理的异常，并返回系统错误的响应。
     *
     * @param e 捕获到的异常
     * @return 包含错误信息的AjaxResult对象
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public AjaxResult handleException(Exception e) {
        log.error("controller exception", e);
        return AjaxResult.fail(BizCode.SYSTEM_ERROR.getComment());
    }

    /**
     * 处理业务运行时异常。
     * 捕获BusinessRuntimeException，并返回相应的错误信息。
     *
     * @param e 捕获到的BusinessRuntimeException
     * @return 包含错误信息的AjaxResult对象
     */
    @ExceptionHandler(BusinessRuntimeException.class)
    @ResponseBody
    public AjaxResult handleBusinessRuntimeException(BusinessRuntimeException e) {
        log.error("business exception", e);
        return AjaxResult.fail(e.getErrorDetailMsg());
    }

    /**
     * 处理方法参数无效异常。
     * 捕获MethodArgumentNotValidException，并返回详细的字段错误信息。
     *
     * @param e 捕获到的MethodArgumentNotValidException
     * @return 包含错误信息的AjaxResult对象
     */
    @SneakyThrows
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public AjaxResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        List<ObjectError> errors = bindingResult.getAllErrors();
        String message = "";
        if (!errors.isEmpty()) {
            // 只显示第一个错误信息
            FieldError fieldError = (FieldError) errors.get(0);
            message = fieldError.getDefaultMessage();
        }
        return AjaxResult.fail(message);
    }

    /**
     * 处理HTTP消息不可读异常。
     * 捕获HttpMessageNotReadableException，特别是InvalidFormatException，并返回详细的字段错误信息。
     *
     * @param e 捕获到的HttpMessageNotReadableException
     * @return 包含错误信息的AjaxResult对象
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseBody
    public AjaxResult handleInvalidFormatException(HttpMessageNotReadableException e) {
        Throwable cause = e.getCause();
        if (cause instanceof InvalidFormatException) {
            InvalidFormatException ife = (InvalidFormatException) cause;
            String fieldName = ife.getPath().stream()
                    .map(JsonMappingException.Reference::getFieldName)
                    .collect(Collectors.joining("."));
            String targetType = ife.getTargetType().getSimpleName();
            String errorMessage = String.format("字段 '%s' 应为 %s 类型", fieldName, targetType);
            return AjaxResult.fail(errorMessage);
        }
        return AjaxResult.fail("请求体格式错误，确保输入的值为正确的类型");
    }

    @ExceptionHandler(MaxUploadSizeExceededException.class)
    @ResponseBody
    public AjaxResult handleMaxSizeException(MaxUploadSizeExceededException e) {
        return AjaxResult.fail("文件大小超过限制，最大支持 100MB");

    }
}
